package scalab.core.component

import scala.collection.immutable.{Set, HashSet}
import scalab.core.gates.{Input, Output, MandatoryInput}
import scalab.core.dependencies.Dependency

//rename to SinkComponent
trait SinkComponent extends Component with Recipient{

	private var ins: List[Input] = Nil
	def inputs: List[Input] = ins
	def providers: List[Component] = inputs flatMap {i => i.providers}
	//def allSourceComponents: List[Component] = allSources map {s => s.component}
	override def productDirs: List[String] = providers flatMap {p => p.productDirs}
	
	private[core] def updateId: String

	def outOfDateSources: Set[Dependency] = {
		var set: Set[Dependency] = HashSet.empty[Dependency]
		inputs foreach {i => 
			set = set ++ i.outOfDateSources
		}
		set
	}

	

  /*protected[core] def cycleFreeUpdate: Boolean =
    super.update &&
    (locallyUpToDate || execute)*/
  //TODO
  //def upToDate, update
  /*
  //more complicated...if source pipe is not up-to-date
    => regenerate deps
  override protected[scalab.core] def upToDate: Boolean =
    super.upToDate && (dependencies forall {=>upToDate})
  
  override protected[scalab.core] def update: Boolean = 
    super.update && (dependencies forall {=>update})
  */

	def mandatoryIn: Input = {
		val self = this
		newInput(new MandatoryInput{
			override val component = self
		})
	}
	def optionalIn: Input = {
		val self = this
		newInput(new Input{
			override val component = self
		})
	}
	private[core] def newInput(in0: Input): Input = {
		ins = in0 :: ins
		in0
	}

	

	/*def f(self: Component) : Boolean = {
		self.ins foreach {i => i.sources foreach {s => fwd(s.component.out)}}
		//fwd(this.out)//check this
		//cs map {c => fwd(c.out)}
		true
	}*/

	//move this elsewhere (no update => no uptodate???)
	//implement later
	/*
	private[core] def upToDate(fresh: List[Buildable]): Pair[Boolean, List[Buildable]] = 
	if(fresh contains this)
		(true, fresh)
	else{
		var newFresh : List[Buildable] = fresh
		(inputs forall {e =>
			//check for fresh here? not that useful...
			val (toDate, moreFresh) = e upToDate fresh
			if(toDate)
				newFresh = newFresh union moreFresh
			toDate
		}, newFresh)
	}*/

	//def hasUniverse: Boolean = inputs exists {i => i.hasUniverse}

	//rename to sources (rename sources component to providers)
	/*def dependencies: Iterable[Dependency] = inputs flatMap {i => i.dependencies} //rename to sources
	def orderedDependencies = Dependency.orderDependencies(dependencies)
	def sourceSet: Set[Dependency] = {
		var set: Set[Dependency] = HashSet.empty[Dependency]
		inputs foreach {i => set = set ++ i.dependencies}
		set
	}*/
	def sources: Iterable[Dependency] = inputs flatMap {i => i.sources}

	final def invalidate(dep: Dependency): Recipient = {
		inputs foreach {i => i.invalidate(dep)}
		this
	}

	final def invalidate(deps: Iterable[Dependency]): Recipient = { 
		inputs foreach {i => i.invalidate(deps)}
		this
	}

	final def lackingSources: List[Dependency] = 
		inputs flatMap {i => i.lackingSources}

	SinkComponent.allSinkComps = SinkComponent.allSinkComps + this
}

object SinkComponent{
	private[core] var allSinkComps: Set[SinkComponent] = HashSet.empty[SinkComponent]
	def getAll: Set[SinkComponent] = allSinkComps
}
